#include <dim-sum/errno.h>
#include <dim-sum/types.h>
#include <dim-sum/bug.h>
#include <dim-sum/mm_types.h>
#include <asm-generic/current.h>
#include <asm/cpu_esr.h>
#include <asm/memfault.h>
#include <dim-sum/sched.h>

/**
 * 内存访问失败处理函数表
 */
static mem_fault_func mem_fault_funtab[IDFSC_MAX] = {
	[0 ... IDFSC_MAX - 1] = mem_fault_unknown,
	[IDFSC_ADDR_SZ_FAULT_L0] = mem_fault_translation,
	[IDFSC_ADDR_SZ_FAULT_L1]  = mem_fault_translation,
	[IDFSC_ADDR_SZ_FAULT_L2] = mem_fault_translation,
	[IDFSC_ADDR_SZ_FAULT_L3] = mem_fault_translation,
	[IDFSC_TRANS_FAULT_L0] = mem_fault_translation,
	[IDFSC_TRANS_FAULT_L1] = mem_fault_translation,
	[IDFSC_TRANS_FAULT_L2] = mem_fault_translation,
	[IDFSC_TRANS_FAULT_L3] = mem_fault_translation,
	[IDFSC_ACCE_FLG_FAULT_L1] = mem_fault_access,
	[IDFSC_ACCE_FLG_FAULT_L2] = mem_fault_access,
	[IDFSC_ACCE_FLG_FAULT_L3] = mem_fault_access,

	[IDFSC_PERMI_FAULT_L1] = mem_fault_access,
	[IDFSC_PERMI_FAULT_L2] = mem_fault_access,
	[IDFSC_PERMI_FAULT_L3] = mem_fault_access,
	[IDFSC_AILGN_FAULT] = mem_fault_alignment,
};

int mem_fault_unknown(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	BUG();
	return -EFAULT;
}

int mem_fault_translation(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	int idfsc; 
	idfsc =  ESR_TO_IDFSC(esr);
	if(idfsc <= IDFSC_ADDR_SZ_FAULT_L3 && idfsc >= IDFSC_ADDR_SZ_FAULT_L0)
		BUG();
	return mem_fault_access(s, fault_addr, esr);
}


int mem_fault_alignment(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	BUG();
	return -EFAULT;
}

int mem_fault_access(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	int ret = 0;
	/**
	 * 在用户态访问用户空间内存
	 */
	if (fault_addr < USER_VA_END)
		if (!(ret = user_vmem_fault(s, fault_addr, esr)))
			return ret;

	/**
	 * 在内核态访问用户空间内存出错
	 */
	if (!user_mode(s) && (ret = kernel_mode_fault(s, fault_addr, esr)))
		return 0;

	pr_info("mem_fault_access addr:%lx current user stack:%lx current user pc:%lx ######, USER_VA_END: %p\n",
		fault_addr, s->sp, s->pc, USER_VA_END);
	BUG();
	return -EFAULT;
}

int mem_fault(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	int ret;
	ret = mem_fault_funtab[ESR_TO_IDFSC(esr)](s, fault_addr, esr);
	if (ret) {
		pr_info("mem_fault addr:%lx current user stack:%lx current user pc:%lx\n", fault_addr, s->sp, s->pc);
		BUG();
	}
	return ret;
}


int user_vmem_fault(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	return vmem_fault(current_mem, fault_addr, esr, s);
}

int kernel_mode_fault(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	printk("%s, task[%s,%p], addr:[%lx], write:[%d]\n", __func__, current->name, current, fault_addr, is_write_abort(esr));
	BUG();
	return 0;
}